﻿using Aeon.Emulator.RuntimeExceptions;

namespace Aeon.Emulator.Instructions.Arithmetic
{
    internal static class Div
    {
        public static readonly EmulatedDivideByZeroException DivideByZero = new EmulatedDivideByZeroException();

        [Opcode("F6/6 rmb", OperandSize = 16 | 32, AddressSize = 16 | 32)]
        public static void ByteDivide(VirtualMachine vm, byte divisor)
        {
            if(divisor == 0)
            {
                vm.RaiseException(DivideByZero);
                return;
            }

            byte byteQ = (byte)((ushort)vm.Processor.AX / divisor);
            byte byteR = (byte)((ushort)vm.Processor.AX % divisor);
            vm.Processor.AL = byteQ;
            vm.Processor.AH = byteR;
        }

        [Opcode("F7/6 rmw", AddressSize = 16 | 32)]
        public static void WordDivide(VirtualMachine vm, ushort divisor)
        {
            if(divisor == 0)
            {
                vm.RaiseException(DivideByZero);
                return;
            }

            uint fullValue = (uint)(((ushort)vm.Processor.DX << 16) | (ushort)vm.Processor.AX);
            ushort wordQ = (ushort)(fullValue / (uint)divisor);
            ushort wordR = (ushort)(fullValue % (uint)divisor);
            vm.Processor.AX = (short)wordQ;
            vm.Processor.DX = (short)wordR;
        }
        [Alternate("WordDivide", AddressSize = 16 | 32)]
        public static void DWordDivide(VirtualMachine vm, uint divisor)
        {
            if(divisor == 0)
            {
                vm.RaiseException(DivideByZero);
                return;
            }

            ulong fullValue = (ulong)((((ulong)(uint)vm.Processor.EDX) << 32) | (ulong)(uint)vm.Processor.EAX);
            uint wordQ = (uint)(fullValue / divisor);
            uint wordR = (uint)(fullValue % divisor);
            vm.Processor.EAX = (int)wordQ;
            vm.Processor.EDX = (int)wordR;
        }
    }

    internal static class IDiv
    {
        [Opcode("F6/7 rmb", OperandSize = 16 | 32, AddressSize = 16 | 32)]
        public static void ByteDivide(VirtualMachine vm, sbyte divisor)
        {
            if(divisor == 0)
            {
                vm.RaiseException(Div.DivideByZero);
                return;
            }

            byte byteQ = (byte)(vm.Processor.AX / divisor);
            byte byteR = (byte)(vm.Processor.AX % divisor);
            vm.Processor.AL = byteQ;
            vm.Processor.AH = byteR;
        }

        [Opcode("F7/7 rmw", AddressSize = 16 | 32)]
        public static void WordDivide(VirtualMachine vm, short divisor)
        {
            if(divisor == 0)
            {
                vm.RaiseException(Div.DivideByZero);
                return;
            }

            int fullValue = ((int)vm.Processor.DX << 16) | (ushort)vm.Processor.AX;
            short wordQ = (short)(fullValue / divisor);
            short wordR = (short)(fullValue % divisor);
            vm.Processor.AX = wordQ;
            vm.Processor.DX = wordR;
        }
        [Alternate("WordDivide", AddressSize = 16 | 32)]
        public unsafe static void DWordDivide(VirtualMachine vm, int divisor)
        {
            if(divisor == 0)
            {
                vm.RaiseException(Div.DivideByZero);
                return;
            }

            long fullValue = (((long)vm.Processor.EDX) << 32) | (uint)vm.Processor.EAX;
            int wordQ = (int)(fullValue / divisor);
            int wordR = (int)(fullValue % divisor);
            vm.Processor.EAX = wordQ;
            vm.Processor.EDX = wordR;
        }
    }
}
